package main

import (
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"runtime"
	"runtime/pprof"
	"sync"
	"time"
)

var wg sync.WaitGroup

// 模拟通道阻塞
func channelBlockingTask(id int) {
	defer wg.Done()
	log.Printf("通道阻塞任务 %d 开始执行", id)

	// 创建一个无缓冲通道
	ch := make(chan int)

	// 尝试从通道接收数据，但没有发送者，导致阻塞
	<-ch

	log.Printf("通道阻塞任务 %d 执行完成", id) // 这行代码永远不会执行
}

// 模拟互斥锁阻塞
func mutexBlockingTask(id int) {
	defer wg.Done()
	log.Printf("互斥锁阻塞任务 %d 开始执行", id)

	// 创建一个互斥锁并锁定
	var mu sync.Mutex
	mu.Lock()

	// 尝试再次锁定同一个互斥锁，导致阻塞
	mu.Lock()

	mu.Unlock()
	log.Printf("互斥锁阻塞任务 %d 执行完成", id)
}

// 模拟条件变量阻塞
func condBlockingTask(id int) {
	defer wg.Done()
	log.Printf("条件变量阻塞任务 %d 开始执行", id)

	// 创建一个条件变量
	var mu sync.Mutex
	cond := sync.NewCond(&mu)

	mu.Lock()
	// 等待条件满足，但没有其他Goroutine发送信号，导致阻塞
	cond.Wait()
	mu.Unlock()

	log.Printf("条件变量阻塞任务 %d 执行完成", id) // 这行代码永远不会执行
}

// 模拟select阻塞
func selectBlockingTask(id int) {
	defer wg.Done()
	log.Printf("select阻塞任务 %d 开始执行", id)

	// 创建两个无缓冲通道
	ch1 := make(chan int)
	ch2 := make(chan int)

	// 使用select等待多个通道，但没有发送者，导致阻塞
	select {
	case <-ch1:
		log.Printf("从ch1接收到数据")
	case <-ch2:
		log.Printf("从ch2接收到数据")
	}

	log.Printf("select阻塞任务 %d 执行完成", id) // 这行代码永远不会执行
}

// 模拟定时阻塞
func timerBlockingTask(id int) {
	defer wg.Done()
	log.Printf("定时阻塞任务 %d 开始执行", id)

	// 创建一个定时器，等待很长时间
	timer := time.NewTimer(10 * time.Minute)

	// 等待定时器触发
	<-timer.C

	log.Printf("定时阻塞任务 %d 执行完成", id)
}

func main() {
	// 启动HTTP服务器，暴露pprof端点
	go func() {
		log.Println(http.ListenAndServe("0.0.0.0:7890", nil))
	}()

	// 设置GOMAXPROCS
	runtime.GOMAXPROCS(runtime.NumCPU())

	// 启用阻塞分析
	runtime.SetBlockProfileRate(1) // 记录所有阻塞事件

	log.Println("阻塞分析示例启动")
	log.Println("访问 http://localhost:7890/debug/pprof/block 查看阻塞信息")

	// 创建通道阻塞的Goroutine
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go channelBlockingTask(i)
	}

	// 创建互斥锁阻塞的Goroutine
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go mutexBlockingTask(i)
	}

	// 创建条件变量阻塞的Goroutine
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go condBlockingTask(i)
	}

	// 创建select阻塞的Goroutine
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go selectBlockingTask(i)
	}

	// 创建定时阻塞的Goroutine
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go timerBlockingTask(i)
	}

	// 定期创建阻塞分析文件
	go func() {
			f, err := os.Create("block.prof")
			if err != nil {
				log.Fatal("could not create block profile: ", err)
			}

			time.Sleep(10 * time.Second) // 等待一段时间，让阻塞发生

			// 获取阻塞分析数据
			if err := pprof.Lookup("block").WriteTo(f, 0); err != nil {
				log.Fatal("could not write block profile: ", err)
			}

			f.Close()
			log.Printf("第 %d 次阻塞分析数据已写入 block.prof", 1)
	}()

	// 程序将一直运行，直到被手动终止
	log.Println("程序将保持运行，直到被手动终止...")
	select {}
}

// 使用方法:
// 1. 运行程序: go run block_example.go
// 2. 访问 http://localhost:7890/debug/pprof/block 查看阻塞信息
// 3. 使用命令行分析: go tool pprof http://localhost:7890/debug/pprof/block
// 4. 或者分析生成的文件: go tool pprof block.prof
// 5. 在pprof交互式命令行中输入: top, web, list等命令
